package com.study.chunguard.common.utils.datatools;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 数据同步工具类
 * <p>
 * 用于处理两个数据集合之间的同步，自动识别需要插入、更新或删除的数据项，
 * 并执行相应的批量操作，确保目标数据集合反映了源数据集合的最新状态。
 *
 * @author zhaowenhao
 * @since 2024-04-01
 */
public class DataSyncUtil {

    private DataSyncUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * 同步数据的通用方法。
     *
     * @param <S>                源数据类型
     * @param <K>                同步关键字类型
     * @param <T>                目标数据类型
     * @param <I>                目标数据ID类型
     * @param sourceData         源数据集合
     * @param targetDataSupplier 获取当前目标数据集合的函数
     * @param convertFunc        将源数据转换为目标数据的函数
     * @param sourceToKeyFunc    从源数据提取同步关键字的函数
     * @param targetToKeyFunc    从目标数据提取同步关键字的函数
     * @param targetToIdFunc     从目标数据获取ID的函数
     * @param setIdFunc          设置目标数据ID的函数
     * @param setKeyFunc         设置目标数据同步关键字的函数
     * @param setCreateTimeFunc  设置目标数据创建时间的函数
     * @param setUpdateTimeFunc  设置目标数据更新时间的函数
     * @param saveBatchFunc      批量保存目标数据的函数
     * @param updateBatchFunc    批量更新目标数据的函数
     * @param removeByIdsFunc    根据ID列表删除目标数据的函数
     */
    @SuppressWarnings({"java:S107", "java:S3864"})
    public static <S, K, T, I> void syncData(
            List<S> sourceData,
            Supplier<List<T>> targetDataSupplier,
            Function<S, T> convertFunc,
            Function<S, K> sourceToKeyFunc,
            Function<T, K> targetToKeyFunc,
            Function<T, I> targetToIdFunc,
            BiConsumer<T, I> setIdFunc,
            BiConsumer<T, K> setKeyFunc,
            BiConsumer<T, Date> setCreateTimeFunc,
            BiConsumer<T, Date> setUpdateTimeFunc,
            Consumer<List<T>> saveBatchFunc,
            Consumer<List<T>> updateBatchFunc,
            Consumer<List<I>> removeByIdsFunc) {

        Date now = new Date();

        // 提取源数据的同步关键字
        Set<K> sourceKeys = sourceData.stream()
                .map(sourceToKeyFunc)
                .collect(Collectors.toSet());

        // 获取目标数据集合
        List<T> targetData = targetDataSupplier.get();

        // 映射目标数据到它们的同步关键字
        Map<K, T> targetDataMap = targetData.stream()
                .collect(Collectors.toMap(targetToKeyFunc, Function.identity(), (a, b) -> a));

        Set<K> targetKeys = targetDataMap.keySet();

        // 确定需要新增的数据
        List<T> toInsert = sourceData.stream()
                .filter(source -> !targetKeys.contains(sourceToKeyFunc.apply((source))))
                .map(source -> {
                    T entity = convertFunc.apply(source);
                    K key = sourceToKeyFunc.apply(source);
                    setIdFunc.accept(entity, null);
                    setKeyFunc.accept(entity, key);
                    setUpdateTimeFunc.accept(entity, now);
                    setCreateTimeFunc.accept(entity, now);
                    return entity;
                })
                .collect(Collectors.toList());

        // 确定需要更新的数据
        List<T> toUpdate = sourceData.stream()
                .filter(source -> targetKeys.contains(sourceToKeyFunc.apply(source)))
                .map(source -> {
                    T existingEntity = targetDataMap.get(sourceToKeyFunc.apply(source));
                    T entity = convertFunc.apply(source);
                    K key = sourceToKeyFunc.apply(source);
                    setIdFunc.accept(entity, targetToIdFunc.apply(existingEntity));
                    setKeyFunc.accept(entity, key);
                    setUpdateTimeFunc.accept(entity, now);
                    return entity;
                })
                .collect(Collectors.toList());

        // 确定需要删除的数据的ID
        List<I> toDelete = targetData.stream()
                .filter(target -> !sourceKeys.contains(targetToKeyFunc.apply(target)))
                .map(targetToIdFunc)
                .collect(Collectors.toList());

        // 执行批量操作
        if (!toInsert.isEmpty()) {
            saveBatchFunc.accept(toInsert);
        }
        if (!toUpdate.isEmpty()) {
            updateBatchFunc.accept(toUpdate);
        }
        if (!toDelete.isEmpty()) {
            removeByIdsFunc.accept(toDelete);
        }
    }
}
